我們在定義類別或結構時,有時候會需要做初始化的動作,簡單說,就是給一個值,譬如我們在寫C的時候,如果我們要對一個值去做增加或減少的時候,我們會先給一個初始值,通常是0,譬如這樣:
int value = 0
for(int i = 0; i<10; i++)
{
value = value + i;
}
透過給初始值的方式,編譯器才知道,我們要累加的值是從0開始。
以此類推,如果我們這樣在class裡面宣告一個變數
這個時候編譯器就會報錯說你沒有初始化,接下來我們把它初始化:
class Example1 {
var heigth = Int()
}
這邊的Int()就是給他一個值為0,當然我們也可以搭配自定義的初始化,例如這樣:
class Example2 {
var height: Int
init(height : Int) {
self.height = height
}
}
透過init(Initialization)這樣一來我們就可以自訂我們的初始化,但須留意的是,我們在這裡只宣告height是個Int型態,並沒有給他初始值,我們加入下面的程式碼
let tom = Example2(height: 180)
print(tom)
這樣一來我們就把180這個值給到height了
指定初始化跟便捷初始化(Designated and Convenience Initializer):
一般我們所使用的init都稱為Designated Initializer,但如果需要遇到補充的參數,而他也需要被初始化的時候,我們就可以使用Convenience Initializer了,譬如這樣:
lass Student{
var height: Double
var weight: Double
var studentName: String
init(height:Double, weight: Double, studentName: String){
self.height = height
self.weight = weight
self.studentName = studentName
}
convenience init(newStudentName: String) {
self.init(height: 175.0, weight: 60.0, studentName: newStudentName)
}
}
然後我們宣告一個變數,然後把裡面的參數打印出來
透過這張圖,我們可以得到一個結果,因為我們已經在convenience init初始化我們的heigth跟weight了,但是我們沒有告訴編譯器studentName是什麼,我們只跟他說,我們用了一個newStudentName而他的值是String,所以,我們在最底下就必續給他一個值(學生姓名),給他值之後,他就會傳給convenience init,然後再給designated init,也就是,假設我們給peter,他就會是 peter -> newStudentName -> studentName,所以我們print是印出studentName~
var peter = Student(newStudentName: "peter")
print(peter.studentName)
//peter
雖然height跟weight,不能重新在下面給值,原因是因為已經被convenience init初始化過了,但是我們可以取用他的值,譬如這樣:
print(peter.height)
//175.0
這時候你會想說,可是我堅持要給height跟weight一個新的值,可不可以?答案是可以,但是我們就是透過一開始的designated init去給他值(上面圖片的第一行(height:weight:studentName:)),所以們連學生名字都要自己取一個新的,譬如:
var tom = Student(height: 154, weight: 162, studentName: "tom")
print(tom.height)
//154.0
當然,一個class我們通常只會使用一個,或兩個designated init,但是convenience init可以在一個class裡面同時多個使用(3個以上),譬如我們在弄一個convenience init並結合之前的 convenience init跟designated init來做個完整的練習:
import UIKit
class Student{
var height: Double
var weight: Double
var studentName: String
init(height:Double, weight: Double, studentName: String){
self.height = height
self.weight = weight
self.studentName = studentName
}
convenience init(newStudentName: String) {
self.init(height: 175.0, weight: 60.0, studentName: newStudentName)
}
convenience init(newStudentName2: String) {
self.init(height: 162, weight: 50.0, studentName: newStudentName2)
}
}
var peter = Student(newStudentName: "peter")
print(peter.studentName)
print("peter's height is \(peter.height) cm")
var tom = Student(height: 154, weight: 162, studentName: "tom")
print(" ")
print(tom.studentName)
print("toms's height is \(tom.height) cm")
var allen = Student(newStudentName2: "allen")
print(" ")
print(allen.studentName)
print("allen's weight is \(allen.weight) kg")
結果如下:
我們透過這個方式可以印證如swift官方文件所說的:
“Designated initializers must always delegate up
Convenience initializers must always delegate across“
這句話用他的圖片來看就是這樣:
這個情況是你有一個父類別,跟一個子類別,父類別裡有一個designated跟兩個convenience init,子類別有兩個designated init 跟一個convenience init,可以觀察下片頭的方向,他在講的就是:
convenience init是橫向(左右)傳值
designated則是縱向(上下)傳值
而當你有多個class則是長這樣
最後補充三個規則:
Rule 1
A designated initializer must call a designated initializer from its immediate superclass.
指定初始化只能從父類別叫另一個指定初始化器
Rule 2
A convenience initializer must call another initializer from the same class.
便捷初始化器調用另一個初始化器(有可能是指定初始化器也可能是另個便捷初始化器),必須是在同一個class
Rule 3
A convenience initializer must ultimately call a designated initializer.
便捷初始化器最終一定會呼叫一個指定初始化器(不管子類別還是父類別)